#pragma once

#include "app.h"

#ifdef WIN32
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>

#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
#include <xinput.h>
typedef DWORD(WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
typedef DWORD(WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE *);
#endif

namespace mu
{
	struct ImGui_ImplWin32_Data
	{
		ImGui_ImplWin32_Data() { memset(this, 0, sizeof(ImGui_ImplWin32_Data)); }
		HWND                        hWnd;
		HWND                        MouseHwnd;
		int                         MouseTrackedArea;   // 0: not tracked, 1: client are, 2: non-client area
		int                         MouseButtonsDown;
		INT64                       Time;
		INT64                       TicksPerSecond;
		ImGuiMouseCursor            LastMouseCursor;
		UINT32                      KeyboardCodePage;

#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
		bool                        HasGamepad;
		bool                        WantUpdateHasGamepad;
		HMODULE                     XInputDLL;
		PFN_XInputGetCapabilities   XInputGetCapabilities;
		PFN_XInputGetState          XInputGetState;
#endif
	};

	static ImGui_ImplWin32_Data *ImGui_ImplWin32_GetBackendData()
	{
		return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data *)ImGui::GetIO().BackendPlatformUserData : nullptr;
	}

	class MusicAppWin32 : public App
	{
	public:
		struct FrameContext
		{
			ID3D12CommandAllocator *CommandAllocator;
			UINT64                  FenceValue;
		};

		MusicAppWin32();
		virtual ~MusicAppWin32();
		virtual int Init();
		virtual int Release();
		virtual int Update();
		virtual bool Loop();

	public:
		bool CreateDeviceD3D(HWND hWnd);
		void CleanupDeviceD3D();
		static void CreateRenderTarget();
		static void CleanupRenderTarget();
		static void WaitForLastSubmittedFrame();
		FrameContext *WaitForNextFrameResources();
	protected:
		static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
		WNDCLASSEXW wc_;
		HWND hWnd_;

		ID3D12DescriptorHeap *g_pd3dRtvDescHeap = nullptr;
		ID3D12DescriptorHeap *g_pd3dSrvDescHeap = nullptr;
		ID3D12CommandQueue *g_pd3dCommandQueue = nullptr;
		ID3D12GraphicsCommandList *g_pd3dCommandList = nullptr;
		UINT64                       g_fenceLastSignaledValue = 0;
		HANDLE                       g_hSwapChainWaitableObject = nullptr;
		static int const                    NUM_FRAMES_IN_FLIGHT = 3;
		static int const                    NUM_BACK_BUFFERS = 3;
		static FrameContext                 g_frameContext[NUM_FRAMES_IN_FLIGHT];
		static UINT                         g_frameIndex;
		static ID3D12Device *g_pd3dDevice;
		static ID3D12Fence *g_fence;
		static HANDLE                       g_fenceEvent;
		static IDXGISwapChain3 *g_pSwapChain;
		static ID3D12Resource *g_mainRenderTargetResource[NUM_BACK_BUFFERS];
		static D3D12_CPU_DESCRIPTOR_HANDLE  g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS];
	};
}

#endif